package com.ittao.taoaicodeapp.core.saver;

import cn.hutool.core.util.StrUtil;
import com.ittao.taoaicodecommon.exception.BusinessException;
import com.ittao.taoaicodecommon.exception.ErrorCode;
import com.ittao.taoaicodecommon.manage.storage.factory.FileStorageStrategyFactory;
import com.ittao.taoaicodecommon.manage.storage.lifecycle.tracker.SessionActivityTracker;
import com.ittao.taoaicodecommon.manage.storage.service.CosOperationService;
import com.ittao.taoaicodecommon.manage.storage.strategy.FileStorageStrategy;
import com.ittao.taoaicodecommon.manage.storage.strategy.impl.CosFileStorageStrategy;
import com.ittao.taoaicodemodel.model.enums.CodeGenTypeEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
 * 抽象代码文件保存器 - 模板方法模式
 * 支持统一的生命周期管理：保存 → 上传 → 清理
 */
@Slf4j
public abstract class CodeFileSaverTemplate<T> {

    @Resource
    private FileStorageStrategyFactory fileStorageStrategyFactory;

    @Resource
    private SessionActivityTracker sessionActivityTracker;

    @Resource
    private CosOperationService cosOperationService;

    /**
     * 模板方法，保存代码的标准流程
     * @param appId 应用 ID
     * @param result 代码生成结果
     * @param userId 用户ID
     * @return 保存的目录结果
     */
    public final CodeSaveResult saveCode(T result, Long appId, Long userId) {
        // 1. 验证输入
        validateInput(result);
        // 2. 构建唯一目录
        String baseDirPath = buildUniqueDir(appId, userId);
        // 3. 保存文件（具体实现交给子类）
        saveFile(result, baseDirPath);

        // 4. 生命周期管理：上传和清理
        handleLifecycleManagement(userId, appId, baseDirPath);

        // 5. 根据存储策略生成正确的显示路径
        FileStorageStrategy storageStrategy = fileStorageStrategyFactory.getCurrentStrategy();
        String displayPath = storageStrategy.buildDisplayPath(baseDirPath, String.valueOf(userId), String.valueOf(appId), getCodeType().getValue());
        // 6. 返回代码保存结果对象
        return new CodeSaveResult(baseDirPath, displayPath, userId, appId);
    }

    /**
     * 保存单个文件
     * @param dirPath 目录路径
     * @param fileName 文件名
     * @param fileContent 文件内容
     */
    public final void writeToFile(String dirPath, String fileName, String fileContent) {
        if (StrUtil.isNotEmpty(fileContent)) {
            try {
                String fullPath = dirPath + "/" + fileName;
                fileStorageStrategyFactory.getCurrentStrategy().writeFile(fullPath, fileContent.getBytes());
            } catch (IOException e) {
                log.error("文件写入失败: {}/{}, 错误: {}", dirPath, fileName, e.getMessage(), e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件保存失败: " + e.getMessage());
            }
        }
    }

    /**
     * 构建文件的唯一路径
     * @param appId 应用 ID
     * @param userId 用户ID
     * @return 唯一目录路径
     */
    protected String buildUniqueDir(Long appId, Long userId) {
        if (appId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        }
        if (userId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户 ID 不能为空");
        }
        String codeType = getCodeType().getValue();
        String projectName = StrUtil.format("{}_{}", codeType, appId);
        FileStorageStrategy storageStrategy = fileStorageStrategyFactory.getCurrentStrategy();
        return storageStrategy.buildCodeOutputPath(String.valueOf(userId), projectName);
    }

    /**
     * 验证输入参数（可由子类覆盖）
     * @param result 代码结果对象
     */
    protected void validateInput(T result) {
        if (result == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "代码结果对象不能为空");
        }
    }

    /**
     * 获取到代码生成的类型
     * @return 代码生成类型
     */
    protected abstract CodeGenTypeEnum getCodeType();

    /**
     * 保存文件的具体实现
     * @param result 代码生成结果
     * @param baseDirPath 保存的根目录
     */
    protected abstract void saveFile(T result, String baseDirPath);

    /**
     * 处理生命周期管理：根据存储策略区分处理逻辑
     * @param userId 用户ID
     * @param appId 应用ID
     * @param baseDirPath 本地文件路径
     */
    private void handleLifecycleManagement(Long userId, Long appId, String baseDirPath) {
        try {
            FileStorageStrategy strategy = fileStorageStrategyFactory.getCurrentStrategy();
            
            if (strategy instanceof CosFileStorageStrategy) {
                // COS策略：完整生命周期管理（上传 + 清理）
                handleCosLifecycle(userId, appId, baseDirPath);
            } else {
                // 本地策略：仅保存，无需上传和清理
                log.info("本地存储策略，文件已保存至: {}", baseDirPath);
            }
        } catch (Exception e) {
            log.error("生命周期管理失败: userId={}, appId={}, path={}, error={}",
                    userId, appId, baseDirPath, e.getMessage(), e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 处理COS存储策略的完整生命周期
     */
    private void handleCosLifecycle(Long userId, Long appId, String baseDirPath) {
        String sessionId = generateSessionId(userId, appId);

        // 1. 发布项目生成开始事件，注册会话监控
        publishGenerationStartEvent(sessionId, userId, appId, baseDirPath);

        // 2. 上传到COS
        uploadToCos(userId, appId, baseDirPath);

        // 3. 发布项目生成完成事件，启动清理任务
        publishGenerationCompleteEvent(sessionId, userId, appId, baseDirPath);

        log.info("COS生命周期管理完成: sessionId={}, userId={}, appId={}, path={}",
                sessionId, userId, appId, baseDirPath);
    }

    /**
     * 上传到COS
     */
    private void uploadToCos(Long userId, Long appId, String baseDirPath) {
        try {
            String codeType = getCodeType().getValue();
            String projectName = codeType + "_" + appId;
            String cosPath = "userApp/" + userId + "/code_output/" + projectName;

            boolean uploadSuccess = cosOperationService.uploadBuildResults(
                    baseDirPath,
                    cosPath
            );

            if (uploadSuccess) {
                log.info("COS上传成功: userId={}, appId={}, cosPath={}", userId, appId, cosPath);
            } else {
                log.warn("COS上传失败: userId={}, appId={}, cosPath={}", userId, appId, cosPath);
            }
        } catch (Exception e) {
            log.error("COS上传异常: userId={}, appId={}, error={}", userId, appId, e.getMessage(), e);
        }
    }

    /**
     * 发布项目生成开始事件
     */
    private void publishGenerationStartEvent(String sessionId, Long userId, Long appId, String baseDirPath) {
        try {
            sessionActivityTracker.publishProjectGenerationStart(
                    sessionId,
                    String.valueOf(userId),
                    String.valueOf(appId),
                    baseDirPath
            );
            log.info("发布生成开始事件: sessionId={}, userId={}, appId={}", sessionId, userId, appId);
        } catch (Exception e) {
            log.error("发布生成开始事件失败: sessionId={}, userId={}, appId={}, error={}",
                    sessionId, userId, appId, e.getMessage(), e);
        }
    }

    /**
     * 发布项目生成完成事件
     */
    private void publishGenerationCompleteEvent(String sessionId, Long userId, Long appId, String baseDirPath) {
        try {
            sessionActivityTracker.publishProjectGenerationComplete(
                    sessionId,
                    String.valueOf(userId),
                    String.valueOf(appId),
                    baseDirPath
            );
            log.info("发布生成完成事件: sessionId={}, userId={}, appId={}", sessionId, userId, appId);
        } catch (Exception e) {
            log.error("发布生成完成事件失败: sessionId={}, userId={}, appId={}, error={}",
                    sessionId, userId, appId, e.getMessage(), e);
        }
    }

    /**
     * 生成会话ID
     */
    private String generateSessionId(Long userId, Long appId) {
        return String.format("%s_%s_%s_%d",
                getCodeType().getValue(), userId, appId, System.currentTimeMillis());
    }
}
